home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / cpp_libs / cool / cool.lha / ice / pisces / rcs / rcsfnms.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-09-04  |  27.3 KB  |  788 lines

  1. /*
  2.  *                     RCS file name handling
  3.  */
  4. #ifndef lint
  5.  static char
  6.  rcsid[]= "$Id: rcsfnms.c,v 1.1 90/05/21 13:43:03 neath Exp $ Purdue CS";
  7. #endif
  8. /****************************************************************************
  9.  *                     creation and deletion of semaphorefile,
  10.  *                     creation of temporary filenames and cleanup()
  11.  *                     pairing of RCS file names and working file names.
  12.  *                     Testprogram: define PAIRTEST
  13.  ****************************************************************************
  14.  */
  15.  
  16. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  17.    Distributed under license by the Free Software Foundation, Inc.
  18.  
  19. This file is part of RCS.
  20.  
  21. RCS is free software; you can redistribute it and/or modify
  22. it under the terms of the GNU General Public License as published by
  23. the Free Software Foundation; either version 1, or (at your option)
  24. any later version.
  25.  
  26. RCS is distributed in the hope that it will be useful,
  27. but WITHOUT ANY WARRANTY; without even the implied warranty of
  28. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  29. GNU General Public License for more details.
  30.  
  31. You should have received a copy of the GNU General Public License
  32. along with RCS; see the file COPYING.  If not, write to
  33. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  34.  
  35. Report problems and direct all questions to:
  36.  
  37.     rcs-bugs@cs.purdue.edu
  38.  
  39. */
  40.  
  41.  
  42.  
  43.  
  44. /* $Log:    rcsfnms.c,v $
  45.  * Revision 1.1  90/05/21  13:43:03  neath
  46.  * Initial revision
  47.  * 
  48.  * Revision 4.8  89/05/01  15:09:41  narten
  49.  * changed getwd to not stat empty directories.
  50.  * 
  51.  * Revision 4.7  88/11/08  12:01:22  narten
  52.  * changes from  eggert@sm.unisys.com (Paul Eggert)
  53.  * 
  54.  * Revision 4.7  88/08/09  19:12:53  eggert
  55.  * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint.
  56.  * 
  57.  * Revision 4.6  87/12/18  11:40:23  narten
  58.  * additional file types added from 4.3 BSD version, and SPARC assembler
  59.  * comment character added. Also, more lint cleanups. (Guy Harris)
  60.  * 
  61.  * Revision 4.5  87/10/18  10:34:16  narten
  62.  * Updating version numbers. Changes relative to 1.1 actually relative
  63.  * to verion 4.3
  64.  * 
  65.  * Revision 1.3  87/03/27  14:22:21  jenkins
  66.  * Port to suns
  67.  * 
  68.  * Revision 1.2  85/06/26  07:34:28  svb
  69.  * Comment leader '% ' for '*.tex' files added.
  70.  * 
  71.  * Revision 1.1  84/01/23  14:50:24  kcs
  72.  * Initial revision
  73.  * 
  74.  * Revision 4.3  83/12/15  12:26:48  wft
  75.  * Added check for KDELIM in file names to pairfilenames().
  76.  * 
  77.  * Revision 4.2  83/12/02  22:47:45  wft
  78.  * Added csh, red, and sl file name suffixes.
  79.  * 
  80.  * Revision 4.1  83/05/11  16:23:39  wft
  81.  * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
  82.  * 1. added copying of path from workfile to RCS file, if RCS file is omitted;
  83.  * 2. added getting the file status of RCS and working files;
  84.  * 3. added ignoring of directories.
  85.  * 
  86.  * Revision 3.7  83/05/11  15:01:58  wft
  87.  * Added comtable[] which pairs file name suffixes with comment leaders;
  88.  * updated InitAdmin() accordingly.
  89.  * 
  90.  * Revision 3.6  83/04/05  14:47:36  wft
  91.  * fixed Suffix in InitAdmin().
  92.  * 
  93.  * Revision 3.5  83/01/17  18:01:04  wft
  94.  * Added getwd() and rename(); these can be removed by defining
  95.  * V4_2BSD, since they are not needed in 4.2 bsd.
  96.  * Changed sys/param.h to sys/types.h.
  97.  *
  98.  * Revision 3.4  82/12/08  21:55:20  wft
  99.  * removed unused variable.
  100.  *
  101.  * Revision 3.3  82/11/28  20:31:37  wft
  102.  * Changed mktempfile() to store the generated file names.
  103.  * Changed getfullRCSname() to store the file and pathname, and to
  104.  * delete leading "../" and "./".
  105.  *
  106.  * Revision 3.2  82/11/12  14:29:40  wft
  107.  * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
  108.  * checksuffix(), checkfullpath(). Semaphore name generation updated.
  109.  * mktempfile() now checks for nil path; freefilename initialized properly.
  110.  * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
  111.  * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
  112.  *
  113.  * Revision 3.1  82/10/18  14:51:28  wft
  114.  * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
  115.  * renamed checkpath() to checkfullpath().
  116.  */
  117.  
  118.  
  119. #include "rcsbase.h"
  120. #include <sys/types.h>
  121. #include <sys/stat.h>
  122. #include <sys/dir.h>
  123.  
  124. extern char * rindex();
  125. extern char * mktemp();
  126. extern FILE * fopen();
  127. extern char * getwd();         /* get working directory; forward decl       */
  128. extern int    stat(), fstat();
  129.  
  130. extern FILE * finptr;          /* RCS input file descriptor                 */
  131. extern FILE * frewrite;        /* New RCS file descriptor                   */
  132. extern char * RCSfilename, * workfilename; /* filenames                     */
  133. struct stat RCSstat, workstat; /* file status for RCS file and working file */
  134. int    haveRCSstat,  haveworkstat; /* indicators if status availalble       */
  135.  
  136.  
  137. char tempfilename [NCPFN+10];  /* used for derived file names               */
  138. char sub1filename [NCPPN];     /* used for files path/file.sfx,v            */
  139. char sub2filename [NCPPN];     /* used for files path/RCS/file.sfx,v        */
  140. char semafilename [NCPPN];     /* name of semaphore file                    */
  141. int  madesema;                 /* indicates whether a semaphore file has been set */
  142. char * tfnames[10];            /* temp. file names to be unlinked when finished   */
  143. int  freefilename;             /* index of next free file name in tfnames[]  */
  144.  
  145.  
  146. struct compair {
  147.         char * suffix, * comlead;
  148. };
  149.  
  150. struct compair comtable[] = {
  151. /* comtable pairs each filename suffix with a comment leader. The comment   */
  152. /* leader is placed before each line generated by the $Log keyword. This    */
  153. /* table is used to guess the proper comment leader from the working file's */
  154. /* suffix during initial ci (see InitAdmin()). Comment leaders are needed   */
  155. /* for languages without multiline comments; for others they are optional.  */
  156.         "c",   " * ",   /* C           */
  157.     "csh", "# ",    /* shell       */
  158.         "e",   "# ",    /* efl         */
  159.         "f",   "c ",    /* fortran     */
  160.         "h",   " * ",   /* C-header    */
  161.         "l",   " * ",   /* lex         NOTE: conflict between lex and franzlisp*/
  162.         "mac", "; ",    /* macro       vms or dec-20 or pdp-11 macro */
  163.     "me",  ".\\\" ",/* me-macros   t/nroff*/
  164.     "mm",  ".\\\" ",/* mm-macros   t/nroff*/
  165.     "ms",  ".\\\" ",/* ms-macros   t/nroff*/
  166.         "p",   " * ",   /* pascal      */
  167.     "pl",  "% ",    /* prolog      */
  168.         "r",   "# ",    /* ratfor      */
  169.         "red", "% ",    /* psl/rlisp   */
  170.  
  171. #ifdef sparc
  172.         "s",   "! ",    /* assembler   */
  173. #endif
  174. #ifdef mc68000
  175.         "s",   "| ",    /* assembler   */
  176. #endif
  177. #ifdef pdp11
  178.         "s",   "/ ",    /* assembler   */
  179. #endif
  180. #ifdef vax
  181.         "s",   "# ",    /* assembler   */
  182. #endif
  183.  
  184.         "sh",  "# ",    /* shell       */
  185.         "sl",  "% ",    /* psl         */
  186.         "red", "% ",    /* psl/rlisp   */
  187.         "cl",  ";;; ",  /* common lisp   */
  188.         "ml",  "; ",    /* mocklisp    */
  189.         "el",  "; ",    /* gnulisp     */
  190.     "tex", "% ",    /* tex           */
  191.         "y",   " * ",   /* yacc        */
  192.         "ye",  " * ",   /* yacc-efl    */
  193.         "yr",  " * ",   /* yacc-ratfor */
  194.         "",    "# ",    /* default for empty suffix */
  195.         nil,   ""       /* default for unknown suffix; must always be last */
  196. };
  197.  
  198.  
  199. ffclose(fptr)
  200. FILE * fptr;
  201. /* Function: checks ferror(fptr) and aborts the program if there were
  202.  * errors; otherwise closes fptr.
  203.  */
  204. {       if (ferror(fptr) || fclose(fptr)==EOF)
  205.                 faterror("File read or write error; file system full?");
  206. }
  207.  
  208.  
  209.  
  210. int trysema(RCSname,makesema)
  211. char * RCSname; int makesema;
  212. /* Function: Checks whether a semaphore file exists for RCSname. If yes,
  213.  * returns false. If not, creates one if makesema==true and returns true
  214.  * if successful. If a semaphore file was created, madesema is set to true.
  215.  * The name of the semaphore file is put into variable semafilename.
  216.  */
  217. {
  218.         register char * tp, *sp, *lp;
  219.         int fdesc;
  220.  
  221.         sp=RCSname;
  222.         lp = rindex(sp,'/');
  223.         if (lp==0) {
  224.                 semafilename[0]='.'; semafilename[1]='/';
  225.                 tp= &semafilename[2];
  226.         } else {
  227.                 /* copy path */
  228.                 tp=semafilename;
  229.                 do *tp++ = *sp++; while (sp<=lp);
  230.         }
  231.         /*now insert `,' and append file name */
  232.         *tp++ = ',';
  233.         lp = rindex(sp, RCSSEP);
  234.         while (sp<lp) *tp++ = *sp++;
  235.         *tp++ = ','; *tp++ = '\0'; /* will be the same length as RCSname*/
  236.  
  237.         madesema = false;
  238.         if (access(semafilename, 0) == 0) {
  239.                 error("RCS file %s is in use",RCSname);
  240.                 return false;
  241.         }
  242.         if (makesema) {
  243.                 if ((fdesc=creat(semafilename, 000)) == -1) {
  244.                      error("Can't create semaphore file for RCS file %s",RCSname);
  245.                      return false;
  246.                 } else
  247.                      VOID close(fdesc);
  248.                      madesema=true;
  249.         }
  250.         return true;
  251. }
  252.  
  253.  
  254. rmsema()
  255. /* Function: delete the semaphore file if madeseam==true;
  256.  * sets madesema to false.
  257.  */
  258. {
  259.         if (madesema) {
  260.                 madesema=false;
  261.                 if (unlink(semafilename) == -1) {
  262.                         error("Can't find semaphore file %s",semafilename);
  263.                 }
  264.         }
  265. }
  266.  
  267.  
  268.  
  269. InitCleanup()
  270. {       freefilename =  0;  /* initialize pointer */
  271. }
  272.  
  273.  
  274. cleanup()
  275. /* Function: closes input file and rewrite file.
  276.  * Unlinks files in tfnames[], deletes semaphore file.
  277.  */
  278. {
  279.         register int i;
  280.  
  281.         if (finptr!=NULL)   VOID fclose(finptr);
  282.         if (frewrite!=NULL) VOID fclose(frewrite);
  283.         for (i=0; i<freefilename; i++) {
  284.             if (tfnames[i][0]!='\0')  VOID unlink(tfnames[i]);
  285.         }
  286.         InitCleanup();
  287.         rmsema();
  288. }
  289.  
  290.  
  291. char * mktempfile(fullpath,filename)
  292. register char * fullpath, * filename;
  293. /* Function: Creates a unique filename using the process id and stores it
  294.  * into a free slot in tfnames. The filename consists of the path contained
  295.  * in fullpath concatenated with filename. filename should end in "XXXXXX".
  296.  * Because of storage in tfnames, cleanup() can unlink the file later.
  297.  * freefilename indicates the lowest unoccupied slot in tfnames.
  298.  * Returns a pointer to the filename created.
  299.  * Example use: mktempfile("/tmp/", somefilename)
  300.  */
  301. {
  302.         register char * lastslash, *tp;
  303.         if ((tp=tfnames[freefilename])==nil)
  304.               tp=tfnames[freefilename] = talloc(NCPPN);
  305.         if (fullpath!=nil && (lastslash=rindex(fullpath,'/'))!=0) {
  306.                 /* copy path */
  307.                 while (fullpath<=lastslash) *tp++ = *fullpath++;
  308.         }
  309.         while (*tp++ = *filename++);
  310.         return (mktemp(tfnames[freefilename++]));
  311. }
  312.  
  313.  
  314.  
  315.  
  316. char * bindex(sp,c)
  317. register char * sp, c;
  318. /* Function: Finds the last occurrence of character c in string sp
  319.  * and returns a pointer to the character just beyond it. If the
  320.  * character doesn't occur in the string, sp is returned.
  321.  */
  322. {       register char * r;
  323.         r = sp;
  324.         while (*sp) {
  325.                 if (*sp++ == c) r=sp;
  326.         }
  327.         return r;
  328. }
  329.  
  330.  
  331.  
  332.  
  333.  
  334. InitAdmin()
  335. /* function: initializes an admin node */
  336. {       register char * Suffix;
  337.         register int i;
  338.  
  339.         Head=Dbranch=nil; AccessList=nil; Symbols=nil; Locks=nil;
  340.         StrictLocks=STRICT_LOCKING;
  341.  
  342.         /* guess the comment leader from the suffix*/
  343.         Suffix=bindex(workfilename, '.');
  344.         if (Suffix==workfilename) Suffix= ""; /* empty suffix; will get default*/
  345.         for (i=0;;i++) {
  346.                 if (comtable[i].suffix==nil) {
  347.                         Comment=comtable[i].comlead; /*default*/
  348.                         break;
  349.                 } elsif (strcmp(Suffix,comtable[i].suffix)==0) {
  350.                         Comment=comtable[i].comlead; /*default*/
  351.                         break;
  352.                 }
  353.         }
  354.         Lexinit(); /* Note: if finptr==NULL, reads nothing; only initializes*/
  355. }
  356.  
  357.  
  358.  
  359. char * findpairfile(argc, argv, fname)
  360. int argc; char * argv[], *fname;
  361. /* Function: Given a filename fname, findpairfile scans argv for a pathname
  362.  * ending in fname. If found, returns a pointer to the pathname, and sets
  363.  * the corresponding pointer in argv to nil. Otherwise returns fname.
  364.  * argc indicates the number of entries in argv. Some of them may be nil.
  365.  */
  366. {
  367.         register char * * next, * match;
  368.         register int count;
  369.  
  370.         for (next = argv, count = argc; count>0; next++,count--) {
  371.                 if ((*next != nil) && strcmp(bindex(*next,'/'),fname)==0) {
  372.                         /* bindex finds the beginning of the file name stem */
  373.                         match= *next;
  374.                         *next=nil;
  375.                         return match;
  376.                 }
  377.         }
  378.         return fname;
  379. }
  380.  
  381.  
  382. int pairfilenames(argc, argv, mustread, tostdout)
  383. int argc; char ** argv; int mustread, tostdout;
  384. /* Function: Pairs the filenames pointed to by argv; argc indicates
  385.  * how many there are.
  386.  * Places a pointer to the RCS filename into RCSfilename,
  387.  * and a pointer to the name of the working file into workfilename.
  388.  * If both the workfilename and the RCS filename are given, and tostdout
  389.  * is true, a warning is printed.
  390.  *
  391.  * If the working file exists, places its status into workstat and
  392.  * sets haveworkstat to 0; otherwise, haveworkstat is set to -1;
  393.  * Similarly for the RCS file and the variables RCSstat and haveRCSstat.
  394.  *
  395.  * If the RCS file exists, it is opened for reading, the file pointer
  396.  * is placed into finptr, and the admin-node is read in; returns 1.
  397.  * If the RCS file does not exist and mustread==true, an error is printed
  398.  * and 0 returned.
  399.  * If the RCS file does not exist and mustread==false, the admin node
  400.  * is initialized to empty (Head, AccessList, Locks, Symbols, StrictLocks, Dbranch)
  401.  * and -1 returned.
  402.  *
  403.  * 0 is returned on all errors. Files that are directories are errors.
  404.  * Also calls InitCleanup();
  405.  */
  406. {
  407.         register char * sp, * tp;
  408.         char * lastsep, * purefname, * pureRCSname;
  409.         int opened, returncode;
  410.         char * RCS1;
  411.     char prefdir[NCPPN];
  412.  
  413.         if (*argv == nil) return 0; /* already paired filename */
  414.     if (rindex(*argv,KDELIM)!=0) {
  415.         /* KDELIM causes havoc in keyword expansion    */
  416.         error("RCS file name may not contain %c",KDELIM);
  417.         return 0;
  418.     }
  419.         InitCleanup();
  420.  
  421.         /* first check suffix to see whether it is an RCS file or not */
  422.         purefname=bindex(*argv, '/'); /* skip path */
  423.         lastsep=rindex(purefname, RCSSEP);
  424.         if (lastsep!= 0 && *(lastsep+1)==RCSSUF && *(lastsep+2)=='\0') {
  425.                 /* RCS file name given*/
  426.                 RCS1=(*argv); pureRCSname=purefname;
  427.                 /* derive workfilename*/
  428.                 sp = purefname; tp=tempfilename;
  429.                 while (sp<lastsep) *tp++ = *sp++; *tp='\0';
  430.                 /* try to find workfile name among arguments */
  431.                 workfilename=findpairfile(argc-1,argv+1,tempfilename);
  432.                 if (strlen(pureRCSname)>NCPFN) {
  433.                         error("RCS file name %s too long",RCS1);
  434.                         return 0;
  435.                 }
  436.         } else {
  437.                 /* working file given; now try to find RCS file */
  438.                 workfilename= *argv;
  439.                 /* derive RCS file name*/
  440.                 sp=purefname; tp=tempfilename;
  441.                 while (*tp++ = *sp++);
  442.                 *(tp-1)=RCSSEP; *tp++=RCSSUF; *tp++='\0';
  443.                 /* Try to find RCS file name among arguments*/
  444.                 RCS1=findpairfile(argc-1,argv+1,tempfilename);
  445.                 pureRCSname=bindex(RCS1, '/');
  446.                 if (strlen(pureRCSname)>NCPFN) {
  447.                         error("working file name %s too long",workfilename);
  448.                         return 0;
  449.                 }
  450.         }
  451.         /* now we have a (tentative) RCS filename in RCS1 and workfilename  */
  452.         /* First, get status of workfilename */
  453.         haveworkstat=stat(workfilename, &workstat);
  454.         if ((haveworkstat==0) && ((workstat.st_mode & S_IFDIR) == S_IFDIR)) {
  455.                 diagnose("Directory %s ignored",workfilename);
  456.                 return 0;
  457.         }
  458.         /* Second, try to find the right RCS file */
  459.         if (pureRCSname!=RCS1) {
  460.                 /* a path for RCSfile is given; single RCS file to look for */
  461.                 finptr=fopen(RCSfilename=RCS1, "r");
  462.                 if (finptr!=NULL) {
  463.                     returncode=1;
  464.                 } else { /* could not open */
  465.                     if (access(RCSfilename,0)==0) {
  466.                         error("Can't open existing %s", RCSfilename);
  467.                         return 0;
  468.                     }
  469.                     if (mustread) {
  470.                         error("Can't find %s", RCSfilename);
  471.                         return 0;
  472.                     } else {
  473.                         /* initialize if not mustread */
  474.                         returncode = -1;
  475.                     }
  476.                 }
  477.         } else {
  478.         /* no path for RCS file name. Prefix it with path of work */
  479.         /* file if RCS file omitted. Make a second name including */
  480.         /* RCSDIR and try to open that one first.                 */
  481.         sub1filename[0]=sub2filename[0]= '\0';
  482.         if (RCS1==tempfilename) {
  483.             /* RCS file name not given; prepend work path */
  484.             sp= *argv; tp= sub1filename;
  485.             while (sp<purefname) *tp++ = *sp ++;
  486.             *tp='\0';
  487.             VOID strcpy(sub2filename,sub1filename); /* second one */
  488.         }
  489.         VOID strcat(sub1filename,RCSDIR);
  490.         VOID strcpy(prefdir,sub1filename); /* preferred directory for RCS file*/
  491.         VOID strcat(sub1filename,RCS1); VOID strcat(sub2filename,RCS1);
  492.  
  493.  
  494.                 opened=(
  495.         ((finptr=fopen(RCSfilename=sub1filename, "r"))!=NULL) ||
  496.         ((finptr=fopen(RCSfilename=sub2filename,"r"))!=NULL) );
  497.  
  498.                 if (opened) {
  499.                         /* open succeeded */
  500.                         returncode=1;
  501.                 } else {
  502.                         /* open failed; may be read protected */
  503.             if ((access(RCSfilename=sub1filename,0)==0) ||
  504.                 (access(RCSfilename=sub2filename,0)==0)) {
  505.                                 error("Can't open existing %s",RCSfilename);
  506.                                 return 0;
  507.                         }
  508.                         if (mustread) {
  509.                 error("Can't find %s nor %s",sub1filename,sub2filename);
  510.                                 return 0;
  511.                         } else {
  512.                                 /* initialize new file. Put into ./RCS if possible, strip off suffix*/
  513.                 RCSfilename= (access(prefdir,0)==0)?sub1filename:sub2filename;
  514.                                 returncode= -1;
  515.                         }
  516.                 }
  517.         }
  518.  
  519.         if (returncode == 1) { /* RCS file open */
  520.                 haveRCSstat=fstat(fileno(finptr),&RCSstat);
  521.                 if ((haveRCSstat== 0) && ((RCSstat.st_mode & S_IFDIR) == S_IFDIR)) {
  522.                         diagnose("Directory %s ignored",RCSfilename);
  523.                         return 0;
  524.                 }
  525.                 Lexinit(); getadmin();
  526.         } else {  /* returncode == -1; RCS file nonexisting */
  527.                 haveRCSstat = -1;
  528.                 InitAdmin();
  529.         };
  530.  
  531.         if (tostdout&&
  532.             !(RCS1==tempfilename||workfilename==tempfilename))
  533.                 /*The last term determines whether a pair of        */
  534.                 /* file names was given in the argument list        */
  535.                 warn("Option -p is set; ignoring output file %s",workfilename);
  536.  
  537.         return returncode;
  538. }
  539.  
  540.  
  541. char * getfullRCSname()
  542. /* Function: returns a pointer to the full path name of the RCS file.
  543.  * Calls getwd(), but only once.
  544.  * removes leading "../" and "./".
  545.  */
  546. {       static char pathbuf[NCPPN];
  547.         static char namebuf[NCPPN];
  548.         static int  pathlength;
  549.  
  550.         register char * realname, * lastpathchar;
  551.         register int  dotdotcounter, realpathlength;
  552.  
  553.         if (*RCSfilename=='/') {
  554.                 return(RCSfilename);
  555.         } else {
  556.                 if (pathlength==0) { /*call curdir for the first time*/
  557.                     if (getwd(pathbuf)==NULL)
  558.                         faterror("Can't build current directory path");
  559.                     pathlength=strlen(pathbuf);
  560.                     if (!((pathlength==1) && (pathbuf[0]=='/'))) {
  561.                         pathbuf[pathlength++]='/';
  562.                         /* Check needed because some getwd implementations */
  563.                         /* generate "/" for the root.                      */
  564.                     }
  565.                 }
  566.                 /*the following must be redone since RCSfilename may change*/
  567.                 /* find how many ../ to remvove from RCSfilename */
  568.                 dotdotcounter =0;
  569.                 realname = RCSfilename;
  570.                 while( realname[0]=='.' &&
  571.                       (realname[1]=='/'||(realname[1]=='.'&&realname[2]=='/'))){
  572.                         if (realname[1]=='/') {
  573.                             /* drop leading ./ */
  574.                             realname += 2;
  575.                         } else {
  576.                             /* drop leading ../ and remember */
  577.                             dotdotcounter++;
  578.                             realname += 3;
  579.                         }
  580.                 }
  581.                 /* now remove dotdotcounter trailing directories from pathbuf*/
  582.                 lastpathchar=pathbuf + pathlength-1;
  583.                 while (dotdotcounter>0 && lastpathchar>pathbuf) {
  584.                     /* move pointer backwards over trailing directory */
  585.                     lastpathchar--;
  586.                     if (*lastpathchar=='/') {
  587.                         dotdotcounter--;
  588.                     }
  589.                 }
  590.                 if (dotdotcounter>0) {
  591.                     error("Can't generate full path name for RCS file");
  592.                     return RCSfilename;
  593.                 } else {
  594.                     /* build full path name */
  595.                     realpathlength=lastpathchar-pathbuf+1;
  596.                     VOID strncpy(namebuf,pathbuf,realpathlength);
  597.                     VOID strcpy(&namebuf[realpathlength],realname);
  598.                     return(namebuf);
  599.                 }
  600.         }
  601. }
  602.  
  603.  
  604.  
  605. int trydiraccess(filename)
  606. char * filename;
  607. /* checks write permission in directory of filename and returns
  608.  * true if writable, false otherwise
  609.  */
  610. {
  611.         char pathname[NCPPN];
  612.         register char * tp, *sp, *lp;
  613.         lp = rindex(filename,'/');
  614.         if (lp==0) {
  615.                 /* check current directory */
  616.                 if (access(".",2)==0)
  617.                         return true;
  618.                 else {
  619.                         error("Current directory not writable");
  620.                         return false;
  621.                 }
  622.         }
  623.         /* copy path */
  624.         sp=filename;
  625.         tp=pathname;
  626.         do *tp++ = *sp++; while (sp<=lp);
  627.         *tp='\0';
  628.         if (access(pathname,2)==0)
  629.                 return true;
  630.         else {
  631.                 error("Directory %s not writable", pathname);
  632.                 return false;
  633.         }
  634. }
  635.  
  636.  
  637.  
  638. #ifndef V4_2BSD
  639. /* rename() and getwd() will be provided in bsd 4.2 */
  640.  
  641.  
  642. int rename(from, to)
  643. char * from, *to;
  644. /* Function: renames a file with the name given by from to the name given by to.
  645.  * unlinks the to-file if it already exists. returns -1 on error, 0 otherwise.
  646.  */
  647. {       VOID unlink(to);      /* no need to check return code; will be caught by link*/
  648.                          /* no harm done if file "to" does not exist            */
  649.         if (link(from,to)<0) return -1;
  650.         return(unlink(from));
  651. }
  652.  
  653.  
  654.  
  655. #define dot     "."
  656. #define dotdot  ".."
  657.  
  658.  
  659.  
  660. char * getwd(name)
  661. char * name;
  662. /* Function: places full pathname of current working directory into name and
  663.  * returns name on success, NULL on failure.
  664.  * getwd is an adaptation of pwd. May not return to the current directory on
  665.  * failure.
  666.  */
  667. {
  668.         FILE    *file;
  669.         struct  stat    d, dd;
  670.         char buf[2];    /* to NUL-terminate dir.d_name */
  671.         struct  direct  dir;
  672.  
  673.         int rdev, rino;
  674.         int off;
  675.         register i,j;
  676.  
  677.         name[off= 0] = '/';
  678.         name[1] = '\0';
  679.         buf[0] = '\0';
  680.         if (stat("/", &d)<0) return NULL;
  681.         rdev = d.st_dev;
  682.         rino = d.st_ino;
  683.         for (;;) {
  684.                 if (stat(dot, &d)<0) return NULL;
  685.                 if (d.st_ino==rino && d.st_dev==rdev) {
  686.                         if (name[off] == '/') name[off] = '\0';
  687.                         chdir(name); /*change back to current directory*/
  688.                         return name;
  689.                 }
  690.                 if ((file = fopen(dotdot,"r")) == NULL) return NULL;
  691.                 if (fstat(fileno(file), &dd)<0) goto fail;
  692.                 chdir(dotdot);
  693.                 if(d.st_dev == dd.st_dev) {
  694.                         if(d.st_ino == dd.st_ino) {
  695.                             if (name[off] == '/') name[off] = '\0';
  696.                             chdir(name); /*change back to current directory*/
  697.                             VOID fclose(file);
  698.                             return name;
  699.                         }
  700.                         do {
  701.                             if (fread((char *)&dir, sizeof(dir), 1, file) !=1)
  702.                                 goto fail;
  703.                         } while (dir.d_ino != d.st_ino);
  704.                 }
  705.                 else do {
  706.                         if(fread((char *)&dir, sizeof(dir), 1, file) != 1) {
  707.                             goto fail;
  708.                         }
  709.                         if (dir.d_ino == 0)
  710.                 dd.st_ino = d.st_ino + 1;
  711.                         else if (stat(dir.d_name, &dd) < 0)
  712.                 goto fail;
  713.                 } while(dd.st_ino != d.st_ino || dd.st_dev != d.st_dev);
  714.                 VOID fclose(file);
  715.  
  716.                 /* concatenate file name */
  717.                 i = -1;
  718.                 while (dir.d_name[++i] != 0);
  719.                 for(j=off+1; j>0; --j)
  720.                         name[j+i+1] = name[j];
  721.                 off=i+off+1;
  722.                 name[i+1] = '/';
  723.                 for(--i; i>=0; --i)
  724.                         name[i+1] = dir.d_name[i];
  725.         } /* end for */
  726.  
  727. fail:   VOID fclose(file);
  728.         return NULL;
  729. }
  730.  
  731.  
  732. #endif
  733.  
  734.  
  735. #ifdef PAIRTEST
  736. /* test program for pairfilenames() and getfullRCSname() */
  737. char * workfilename, *RCSfilename;
  738. extern int quietflag;
  739.  
  740. main(argc, argv)
  741. int argc; char *argv[];
  742. {
  743.         int result;
  744.         int initflag,tostdout;
  745.         quietflag=tostdout=initflag=false;
  746.         cmdid="pair";
  747.  
  748.         while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
  749.                 switch ((*argv)[1]) {
  750.  
  751.                 case 'p':       tostdout=true;
  752.                                 break;
  753.                 case 'i':       initflag=true;
  754.                                 break;
  755.                 case 'q':       quietflag=true;
  756.                                 break;
  757.                 default:        error("unknown option: %s", *argv);
  758.                                 break;
  759.                 }
  760.         }
  761.  
  762.         do {
  763.                 RCSfilename=workfilename=nil;
  764.                 result=pairfilenames(argc,argv,!initflag,tostdout);
  765.                 if (result!=0) {
  766.                      diagnose("RCSfile: %s; working file: %s",RCSfilename,workfilename);
  767.                      diagnose("Full RCS file name: %s", getfullRCSname());
  768.                 }
  769.                 switch (result) {
  770.                         case 0: continue; /* already paired file */
  771.  
  772.                         case 1: if (initflag) {
  773.                                     error("RCS file %s exists already",RCSfilename);
  774.                                 } else {
  775.                                     diagnose("RCS file %s exists",RCSfilename);
  776.                                 }
  777.                                 VOID fclose(finptr);
  778.                                 break;
  779.  
  780.                         case -1:diagnose("RCS file does not exist");
  781.                                 break;
  782.                 }
  783.  
  784.         } while (++argv, --argc>=1);
  785.  
  786. }
  787. #endif
  788.